#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
数据预处理模块
负责对导入的数据进行清洗、标准化和初步分析
"""

import pandas as pd
from src.config.constants import DATA_VALIDATION_RULES, PACKAGE_TYPE, FIELD_NAMES
from src.utils.excel_utils import ExcelUtils


class DataPreprocessor:
    """负责对导入的数据进行清洗、标准化和初步分析"""

    def __init__(self):
        """初始化数据预处理器"""
        self.excel_utils = ExcelUtils()

    def clean_data(self, data):
        """清洗数据，去除无效数据，修正格式错误

        Args:
            data: 待清洗的数据

        Returns:
            清洗后的数据

        Raises:
            DataValidationError: 数据验证失败
        """
        if data is None or data.empty:
            return pd.DataFrame()

        # 创建数据副本以避免修改原始数据
        cleaned_data = data.copy()

        # 去除完全为空的行
        cleaned_data = cleaned_data.dropna(how="all")

        # 去除重复行
        cleaned_data = cleaned_data.drop_duplicates()

        # 处理数量字段
        if "数量" in cleaned_data.columns:
            # 移除数量为负值或零的行
            min_quantity = DATA_VALIDATION_RULES.get("MIN_QUANTITY", 0)
            cleaned_data = cleaned_data[cleaned_data["数量"] > min_quantity]

            # 确保数量为整数
            cleaned_data["数量"] = cleaned_data["数量"].astype(int)

        # 处理日期字段
        if "预完工日期" in cleaned_data.columns:
            # 确保日期格式正确
            cleaned_data["预完工日期"] = pd.to_datetime(
                cleaned_data["预完工日期"], errors="coerce"
            )
            # 移除日期无效的行
            cleaned_data = cleaned_data.dropna(subset=["预完工日期"])

        # 处理客户简称字段
        if "客户简称" in cleaned_data.columns:
            # 去除空格和标准化
            cleaned_data["客户简称"] = (
                cleaned_data["客户简称"].str.strip().str.upper()
            )

        # 处理规格型号字段
        if "规格型号" in cleaned_data.columns:
            # 去除空格和标准化
            cleaned_data["规格型号"] = cleaned_data["规格型号"].str.strip()

        # 处理备注字段（如果存在）
        if "备注" in cleaned_data.columns:
            # 填充空值
            cleaned_data["备注"] = cleaned_data["备注"].fillna("")

        # 重置索引
        cleaned_data = cleaned_data.reset_index(drop=True)

        return cleaned_data

    def standardize_data(self, cleaned_data):
        """标准化数据格式

        Args:
            cleaned_data: 清洗后的数据

        Returns:
            标准化后的数据
        """
        if cleaned_data is None or cleaned_data.empty:
            return pd.DataFrame()

        # 创建数据副本
        standardized_data = cleaned_data.copy()

        # 标准化日期格式
        if "预完工日期" in standardized_data.columns:
            standardized_data["预完工日期"] = standardized_data[
                "预完工日期"
            ].dt.strftime("%Y-%m-%d")

        # 标准化数值格式
        numeric_columns = ["数量", "重量"]
        for col in numeric_columns:
            if col in standardized_data.columns:
                standardized_data[col] = pd.to_numeric(
                    standardized_data[col], errors="coerce"
                )

        # 确保必需字段存在
        required_columns = ["客户简称", "规格型号", "数量", "预完工日期"]
        for col in required_columns:
            if col not in standardized_data.columns:
                standardized_data[col] = pd.NA

        return standardized_data

    def analyze_data(self, data):
        """对数据进行初步分析

        Args:
            data: 待分析的数据

        Returns:
            分析结果和统计信息
        """
        if data is None or data.empty:
            return {
                "total_records": 0,
                "unique_customers": 0,
                "unique_specifications": 0,
                "total_quantity": 0,
                "date_range": None,
            }

        # 统计信息
        analysis_result = {
            "total_records": len(data),
            "unique_customers": (
                data["客户简称"].nunique() if "客户简称" in data.columns else 0
            ),
            "unique_specifications": (
                data["规格型号"].nunique() if "规格型号" in data.columns else 0
            ),
            "total_quantity": (
                data["数量"].sum() if "数量" in data.columns else 0
            ),
            "date_range": {
                "min": (
                    data["预完工日期"].min()
                    if "预完工日期" in data.columns
                    else None
                ),
                "max": (
                    data["预完工日期"].max()
                    if "预完工日期" in data.columns
                    else None
                ),
            },
        }

        return analysis_result

    def process_historical_data(self, historical_data):
        """处理历史包装数据，添加包装类型信息

        Args:
            historical_data: 历史包装数据

        Returns:
            添加了包装类型信息的历史数据
        """
        if historical_data is None or historical_data.empty:
            return pd.DataFrame()

        # 创建数据副本
        processed_data = historical_data.copy()

        # 识别包装类型
        processed_data["包装类型"] = self._identify_package_type(
            processed_data
        )

        return processed_data

    def _identify_package_type(self, data, merged_cells_info=None):
        """识别包装类型

        根据项目大纲2.3.1.3节定义：
        - 组合包装：当"包装箱方案"列的某单元格为合并单元格时，该合并单元格所包含的所有行构成一个组合包装
        - 独立包装：当"包装箱方案"列的某单元格为非合并单元格时，该行构成一个独立包装

        Args:
            data: 待识别的数据
            merged_cells_info: 合并单元格信息

        Returns:
            包装类型数组（仅包含独立包装和组合包装两种类型）
        """
        # 初始化所有行为独立包装类型
        package_types = [PACKAGE_TYPE["INDEPENDENT"]] * len(data)

        # 如果没有合并单元格信息或数据为空，返回默认值
        if merged_cells_info is None or data.empty:
            return package_types

        # 检查是否有"包装箱方案"列
        if "包装箱方案" not in data.columns:
            return package_types

        # 查找"包装箱方案"列的索引
        package_scheme_col_index = None
        for i, col_name in enumerate(data.columns):
            if col_name == "包装箱方案":
                package_scheme_col_index = i
                break

        if package_scheme_col_index is None:
            return package_types

        # 处理合并单元格信息，标记组合包装
        for merged_cell in merged_cells_info:
            # 只处理"包装箱方案"列的合并单元格
            if (
                merged_cell["column_name"] == "包装箱方案"
                and merged_cell["min_col"] == merged_cell["max_col"]
                and merged_cell["min_col"] == package_scheme_col_index
            ):
                # 标记该合并单元格内的所有行为组合包装
                for row_idx in range(
                    merged_cell["min_row"], merged_cell["max_row"] + 1
                ):
                    if row_idx < len(package_types):
                        package_types[row_idx] = PACKAGE_TYPE["COMBINATION"]

        return package_types

    def process_historical_data_with_merged_cells(
        self, historical_data, merged_cells_info
    ):
        """处理历史包装数据，添加包装类型信息（使用合并单元格信息）

        Args:
            historical_data: 历史包装数据
            merged_cells_info: 合并单元格信息

        Returns:
            添加了包装类型信息的历史数据
        """
        if historical_data is None or historical_data.empty:
            return pd.DataFrame()

        # 创建数据副本
        processed_data = historical_data.copy()

        # 识别包装类型
        processed_data["包装类型"] = self._identify_package_type(
            processed_data, merged_cells_info
        )

        return processed_data

    def select_features(self, data):
        """选择与包装方案匹配相关的特征

        Args:
            data: 待处理的数据

        Returns:
            选择特征后的数据
        """
        # 实现特征选择逻辑，保留关键字段
        relevant_features = [
            FIELD_NAMES["CUSTOMER_ABBREVIATION"],
            FIELD_NAMES["SPECIFICATION_MODEL"],
            FIELD_NAMES["QUANTITY"],
            FIELD_NAMES["ESTIMATED_COMPLETION_DATE"],
            # 其他关键特征
        ]
        return data[[col for col in relevant_features if col in data.columns]]

    def handle_outliers(self, data):
        """处理数据中的异常值

        Args:
            data: 待处理的数据

        Returns:
            处理异常值后的数据
        """
        # 创建副本以避免修改原数据
        processed_data = data.copy()
        
        # 实现基于统计的异常值检测和处理
        if "数量" in processed_data.columns:
            # 使用IQR方法检测异常值
            Q1 = processed_data["数量"].quantile(0.25)
            Q3 = processed_data["数量"].quantile(0.75)
            IQR = Q3 - Q1
            lower_bound = Q1 - 1.5 * IQR
            upper_bound = Q3 + 1.5 * IQR
            
            # 标记异常值，但保留记录以便后续人工审核
            processed_data["数量_异常标记"] = ((processed_data["数量"] < lower_bound) | (processed_data["数量"] > upper_bound)).astype(int)
        
        return processed_data

    def generate_preprocessing_report(self, original_data, processed_data):
        """生成预处理结果报告

        Args:
            original_data: 原始数据
            processed_data: 处理后的数据

        Returns:
            预处理结果报告字典
        """
        report = {
            "原始记录数": len(original_data),
            "处理后记录数": len(processed_data),
            "删除的记录数": len(original_data) - len(processed_data),
            "数据保留率": f"{len(processed_data) / len(original_data) * 100:.2f}%" if len(original_data) > 0 else "0%"
            # 其他统计信息
        }
        return report

    def preprocess_task_data(self, task_data):
        """预处理任务数据

        Args:
            task_data: 任务数据

        Returns:
            预处理后的任务数据
        """
        # 清洗任务数据
        cleaned_data = self.clean_data(task_data)

        # 标准化任务数据
        standardized_data = self.standardize_data(cleaned_data)
        
        # 特征选择
        selected_data = self.select_features(standardized_data)
        
        # 处理异常值
        processed_data = self.handle_outliers(selected_data)

        # 添加产品归类列，值为空
        processed_data["产品归类"] = ""
        
        # 添加备注字段，值为空（与产品归类相同的方式添加）
        processed_data["备注"] = ""

        # 生成预处理报告
        preprocessing_report = self.generate_preprocessing_report(task_data, processed_data)
        
        # 记录预处理报告（实际应用中可能需要日志记录或返回报告）
        import logging
        logger = logging.getLogger("data_preprocessing")
        logger.info(f"预处理报告: {preprocessing_report}")

        return processed_data

    def preprocess_historical_data(self, historical_data):
        """预处理历史数据

        Args:
            historical_data: 历史数据

        Returns:
            预处理后的历史数据
        """
        # 清洗历史数据
        cleaned_data = self.clean_data(historical_data)

        # 标准化历史数据
        standardized_data = self.standardize_data(cleaned_data)
        
        # 特征选择
        selected_data = self.select_features(standardized_data)
        
        # 处理异常值
        processed_data = self.handle_outliers(selected_data)

        # 处理历史包装数据（使用默认实现，因为没有合并单元格信息）
        processed_data = self.process_historical_data(processed_data)
        
        # 添加备注字段，值为空（与产品归类相同的方式添加）
        processed_data["备注"] = ""
        
        # 生成预处理报告
        preprocessing_report = self.generate_preprocessing_report(historical_data, processed_data)
        
        # 记录预处理报告
        import logging
        logger = logging.getLogger("data_preprocessing")
        logger.info(f"历史数据预处理报告: {preprocessing_report}")

        return processed_data

    def preprocess_historical_data_from_file(
        self, file_path, sheet_name=0, header=0
    ):
        """从文件中预处理历史数据

        这个方法会读取Excel文件并保留合并单元格信息，然后基于合并单元格特征判断包装类型

        Args:
            file_path: Excel文件路径
            sheet_name: 工作表名称或索引，默认为0
            header: 表头行索引，默认为0

        Returns:
            预处理后的历史数据
        """
        # 读取Excel文件并保留合并单元格信息
        df, merged_cells_info = (
            self.excel_utils.read_excel_with_merged_cells_info(
                file_path, sheet_name=sheet_name, header=header
            )
        )

        # 清洗历史数据
        cleaned_data = self.clean_data(df)

        # 标准化历史数据
        standardized_data = self.standardize_data(cleaned_data)
        
        # 特征选择
        selected_data = self.select_features(standardized_data)
        
        # 处理异常值
        processed_data = self.handle_outliers(selected_data)

        # 处理历史包装数据（使用合并单元格信息）
        processed_data = self.process_historical_data_with_merged_cells(
            processed_data, merged_cells_info
        )
        
        # 添加备注字段，值为空（与产品归类相同的方式添加）
        processed_data["备注"] = ""
        
        # 生成预处理报告
        preprocessing_report = self.generate_preprocessing_report(df, processed_data)
        
        # 记录预处理报告
        import logging
        logger = logging.getLogger("data_preprocessing")
        logger.info(f"文件历史数据预处理报告: {preprocessing_report}")

        return processed_data
